///////////////////////////////////////////////////////////////////////////////
/// @file server_object.h
/// @author -- Benjamin J. Orr
/// @brief -- Server class for chat program
///////////////////////////////////////////////////////////////////////////////

#ifndef SERVEROBJECT_H
#define SERVEROBJECT_H

/////////////////////////////
/// LIBS
/////////////////////////////
#include <iostream>
using std::cerr;
using std::endl;
#include <string>
using std::string;
#include <vector>
using std::vector;
#include <pthread.h>

//network functions
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <netinet/in.h>

class Server
{
  public:
    ///////////////////////////////////////////////////////////////////////////////
    /// @fn Server()
    /// @brief Constructor for Server object
    /// @pre None!
    /// @post Creates object Server
    /// @return N/A
    ///////////////////////////////////////////////////////////////////////////////
    Server();

    ///////////////////////////////////////////////////////////////////////////////
    /// @fn ~Server()
    /// @brief Destructor for Server object
    /// @pre A server must exist
    /// @post You've got fewer Server objects
    /// @return N/A
    ///////////////////////////////////////////////////////////////////////////////
    ~Server();

    ///////////////////////////////////////////////////////////////////////////////
    /// @fn void init()
    /// @brief Initializes network communication
    /// @pre A Server object must exist
    /// @param The port where the server will be communicating
    /// @post The server is ready to communicate with a client
    /// @return void
    ///////////////////////////////////////////////////////////////////////////////
    void init(int port);

    ///////////////////////////////////////////////////////////////////////////////
    /// @fn void run()
    /// @brief Starts the Server object
    /// @pre A Server object must exist, init() should be run
    /// @param None.
    /// @post The server is running
    /// @return N/A
    ///////////////////////////////////////////////////////////////////////////////
    void run();

  protected:
    ///////////////////////////////////////////////////////////////////////////////
    /// @fn void signal_interceptor(int sig)
    /// @brief This handles signals
    /// @pre A Server object must exist
    /// @param int sig -- Signal to be intercepted
    /// @post The signal is handled correctly
    /// @return N/A
    ///////////////////////////////////////////////////////////////////////////////
    static void signal_interceptor(int sig);

    ///////////////////////////////////////////////////////////////////////////////
    /// @fn void thread_listen()
    /// @brief Wraps void Server::listen() for threads to use
    /// @pre A Server object exists and is running
    /// @post Wraps
    /// @return NULL
    //////////////////////////////////////////////////////////////////////////////.
    static void * thread_listener(void * serv);

    ///////////////////////////////////////////////////////////////////////////////
    /// @fn void listen()
    /// @brief Listens for connections and generates threads to handle them
    /// @pre A Server object has been created and initialized
    /// @param none
    /// @post Threads are generated by this function for each connection
    /// @return N/A
    ///////////////////////////////////////////////////////////////////////////////
    void listener();

    ///////////////////////////////////////////////////////////////////////////////
    /// @fn void thread_input()
    /// @brief Wraps void Server::input() for threads to use
    /// @pre A Server object must exist and be initialized
    /// @post Wraps
    /// @return N/A
    ///////////////////////////////////////////////////////////////////////////////
    static void * thread_input(void *serv);

    ///////////////////////////////////////////////////////////////////////////////
    /// @fn void input();
    /// @brief Reads from stdin and routes messages to either command handlers
    ///        or the chatroom
    /// @pre A Server object must exist and be initiated,
    ///      an input thread must be spawned
    ///////////////////////////////////////////////////////////////////////////////
    void input();

    ///////////////////////////////////////////////////////////////////////////////
    /// @fn void parse_command(int socket,string cmd)
    /// @brief Parses commands from both the server and the clients, NOTE: this
    ///        function may be called erroniously, that's fine.
    /// @pre A Server object must exist and be initiated
    /// @post Commands to the server are handled
    /// @param int socket -- The source of the command
    /// @param string cmd -- The string to be compared to known commands
    /// @return N/A
    ///////////////////////////////////////////////////////////////////////////////
    void parse_command(int socket,string cmd);

    ///////////////////////////////////////////////////////////////////////////////
    /// @fn void wait_to_die()
    /// @brief A shutdown timer thread for the server
    /// @pre you WANT the server to shut down
    /// @post The server IS shut down
    /// @return N/A
    ///////////////////////////////////////////////////////////////////////////////
    void wait_to_die();

    ///////////////////////////////////////////////////////////////////////////////
    /// @fn void thread_wait_to_die()
    /// @brief A wrapper for wait_to_die
    /// @pre A Server object must exist and be initiated
    /// @post Wraps
    /// @return NULL
    ///////////////////////////////////////////////////////////////////////////////
    static void * thread_wait_to_die(void * serv);

    ///////////////////////////////////////////////////////////////////////////////
    /// @fn void set_username(const int socket, const string &username)
    /// @brief Sets the username of the client specified by the socket
    /// @pre A Server object exists and has been initiated
    /// @pre A Client is connected
    /// @param const int socket -- The socket identifier of the user
    /// @param const string &username -- The new username
    /// @post See brief
    /// @return None
    ///////////////////////////////////////////////////////////////////////////////
    void set_username(const int socket, const string &username);

    ///////////////////////////////////////////////////////////////////////////////
    /// @fnstring get_username(int sock)
    /// @brief Retrieves the username from m_client_usernames
    /// @pre A Server object must exist and have been initiated
    /// @pre There must be at least one client
    /// @param int sock -- The socket number of the user
    /// @return The string 'username'
    ///////////////////////////////////////////////////////////////////////////////
    string get_username(int sock);

    ///////////////////////////////////////////////////////////////////////////////
    /// @fn void * thread_read()
    /// @brief Wraps void Server::read()
    /// @pre A Server object exists and is running
    /// @post Wraps
    /// @return NULL
    ///////////////////////////////////////////////////////////////////////////////
    static void * thread_read(void * serv);

    ///////////////////////////////////////////////////////////////////////////////
    /// @fn void read_in(nt socket)
    /// @brief Allows the server to read from its clients
    /// @pre A Server object must exist and be initiated
    /// @param int socket -- The socket that's to be read from
    /// @return N/A
    ///////////////////////////////////////////////////////////////////////////////
    void read_in(int socket);

    ///////////////////////////////////////////////////////////////////////////////
    ///
    ///////////////////////////////////////////////////////////////////////////////
    void write_out(int socket, string msg);

    ///////////////////////////////////////////////////////////////////////////////
    /// @fn void garbage_man(int socket)
    /// @brief Removes the client indicated by socket from the various lists of
    ///        client vectors
    /// @pre A Server object must exist with at least one client
    /// @param int socket -- The socket that describes the client who is to be
    ///        removed
    /// @post The client is removed from the list and its threads are shut down
    /// @return N/A
    ///////////////////////////////////////////////////////////////////////////////
    void garbage_man(int socket);
    

  private:

    //garbage_man()'s mutex
    pthread_mutex_t          m_client_var_lock;

    struct addrinfo          *m_res;

    //socket file descriptor from socket()
    int                      m_sock_fd;
    //vector of client sockets
    vector<int>              m_client_sockets;
    
    //Server's username
    string                   m_username;
    //list of client usernames
    vector<string>           m_client_usernames;

    //unused?
    sockaddr_in              m_address;
    //
    vector<sockaddr_in>      m_client_addresses;

    //the input thread handle
    pthread_t                m_thread_input;
    //the output thread handle
    pthread_t                m_thread_listener;
    //the pause thread
    pthread_t                m_thread_pause;
    //the vector of client threads
    vector<pthread_t>        m_thread_clients;

    //continuation var
    bool                     m_running; 

};


#endif











